from PyQt4 import QtGui, QtCore
import osg2
import OpenGL.GL
import field_container_view_base
import lucid.core
from lucid.util.qt.property_tree import *
from pydispatch import dispatcher
import lucid.util.osg_helpers as osg_helpers

import lucid.plugins.field_editors.field_editors as field_editors
from lucid.plugins.fc_editor.fc_editors import buildFcEditorPlugin


class FieldContainerViewPlugin(lucid.core.IViewPlugin):
   """ Main view plugin.
       - Create field container view widget
       - Controls the selection of new field contailers and looks for scene changes.
       - Controls the setup of the treemodel and tree view for the field container
          - Controls the model and sets up the view
   """
   def __init__(self):
      lucid.core.IViewPlugin.__init__(self)
      self.widget = FCViewWidget(self)      
      
      # Allocate model and setup view
      fc_list = self.widget.mFCList
      self.mFCListModel = FCListModel(self)
      fc_list.setModel(self.mFCListModel)

      type_combo = self.widget.mTypeCombo
      self.mFilterTypes = osg_helpers.getDerivedTypes(osg2.AttachmentContainer)
      self.mFilterTypes.extend(osg_helpers.getDerivedTypes(osg2.FieldContainerAttachment))
      type_sort = [(c.__name__, c) for c in self.mFilterTypes]
      type_sort.sort()
      self.mFilterTypes[:] = [x[1] for x in type_sort]
      
      top_of_list = ["Material","NodeCore","Light","Group","Node","Image",
                             "AttachmentContainer","FieldContainerAttachment"]
      
      # Add in items, but add the items from top_of_list first.
      for top_pass in [True,False]:
         for i in range(0,len(self.mFilterTypes)):
            type_name = self.mFilterTypes[i].__name__            
            if ((type_name in top_of_list) and top_pass) or \
               ((type_name not in top_of_list) and not top_pass):
               type_combo.addItem(type_name, QtCore.QVariant(int(i)))
         
      type_combo.connect(type_combo, QtCore.SIGNAL("activated(int)"), self.onTypeSelected)
      type_combo.setEditable(False)
      type_combo.setMaxVisibleItems(20)
      self.mFilterType = osg2.NodeCore
      
      # Register callbacks
      #dispatcher.connect(self.onSceneAdd, lucid.core.signal.SceneAdded)
      dispatcher.connect(self.onSceneActivated, lucid.core.signal.ActiveSceneChanged)
      dispatcher.connect(self.onFCChanged, lucid.core.signal.SelectedFieldContainerChanged)
      dispatcher.connect(self.onTypeMapChanged, lucid.core.signal.TypeMapChanged)
      
      fc_list.connect(fc_list.selectionModel(),
         QtCore.SIGNAL("selectionChanged(QItemSelection,QItemSelection)"), self.onFCSelected)
      self.widget.connect(self.widget.mCreateFCBtn, QtCore.SIGNAL("clicked(bool)"),
                          self.onAddFC)
      self.widget.connect(self.widget.mDelFCBtn, QtCore.SIGNAL("clicked(bool)"),
                          self.onDeleteFC)      
      
      # Manually trigger activation of scene to get view up and running
      self.onSceneActivated()

   
   @staticmethod
   def getName():
      return "Field Container Editor"
      
   def getViewWidget(self):
      return self.widget
      
   def onSceneActivated(self):
      """ Called when a new scene is activated.
          Updates the field container list and sets everything to edit
          field containers from the new scene.
      """
      env = lucid.core.Environment()
      active_scene = env.activeScene
      self.mFCListModel.setScene(active_scene)
      lucid.core.Environment().selectedFC = None
   
   def onFCChanged(self):
      """ Called when the selected field container changes. 
          Changes the fc tree to allow editing of the new fc.
      """
      # Create and fcd editor and put it in place      
      fc = lucid.core.Environment().selectedFC
      
      new_plugin = None
      if fc:
         fc_type = type(fc)
         new_plugin = buildFcEditorPlugin(fc_type)
         new_plugin.setFieldContainer(fc)      
      self.setFcEditPlugin(new_plugin)
   
   def onTypeMapChanged(self):
      # Type map changed, so must tell the model that it should get it's list again.      
      self.mFCListModel.updateFCList()
      
   def onTypeSelected(self, index):
      """ Called when a new filter type is selected. """
      item_data = self.widget.mTypeCombo.itemData(index)      
      selected_index = item_data.toInt()[0]
      selected_type = self.mFilterTypes[selected_index]
      print "Selected type: ", selected_type
      self.mFilterType = selected_type
      self.mFCListModel.updateFCList()
   
   # ---- FCs and FC List ---- #
   def onFCSelected(self, selected, deselected):      
      """ Called when a fc is selected in the fc list.
          If the fc is different, sets the selected fc.
      """
      if len(selected.indexes()) > 0:
         index = selected.indexes()[0]
         selected_fc = self.mFCListModel.data(index, QtCore.Qt.UserRole)
         print "FC selected: ", selected_fc
         lucid.core.Environment().selectedFC = selected_fc
   
   def onAddFC(self):
      print "Add fc"
      insert_menu = self.buildAddFCMenu()
      insert_menu.exec_(QtGui.QCursor.pos())
      
   def onDeleteFC(self):
      print "Delete fc (to be implemented)"
   
   def createFC(self,fcType):
      """ Callback for the add fc menu. """
      print "Creating new fc: ", fcType
      new_fc = fcType.create()
      active_scene = lucid.core.Environment().activeScene
      if active_scene:
         active_scene.addToPool(new_fc)


   def buildAddFCMenu(self):
      """ Build up the menu of items that could be added. """      
      fc_types = [c for c in osg_helpers.getDerivedTypes(self.mFilterType) if hasattr(c,"create")]
      
      self._fc_create_calls = []
      create_menu = QtGui.QMenu("Create")
      for fct in fc_types:
         create_call = lambda fctype=fct: self.createFC(fctype)
         self._fc_create_calls.append(create_call)
         create_menu.addAction(fct.__name__, create_call)
      
      return create_menu
      
      
   def setFcEditPlugin(self, plugin):
      " Helper function called to set a new plugin for editing the field container. "
      if None == plugin:           
         self.widget.setEditorWidget(None)
      else:         
         self.widget.setEditorWidget(plugin.getEditorWidget())


class FCViewWidget(QtGui.QWidget, field_container_view_base.Ui_Form):
   """ The Qt widget used by the plugin for field container editing.
       Contains a fc list, fc edit panel, and a window
       for rendering the representation of an object.
   """
   def __init__(self, plugin, parent=None):
      QtGui.QWidget.__init__(self, parent)
      self.setupUi(self)
      self.plugin = plugin
      self.mCurEditorWidget = None  # The current editor widget being used
   
   def setupUi(self, form):
      """ Complete local setup and customizations. """
      field_container_view_base.Ui_Form.setupUi(self, form)      
   
   def setEditorWidget(self, widget):
      """ Called to set the editor widget. """
      # If we are setting to null and we already have empty widget
      if not widget and not self.mCurEditorWidget:
         return
            
      # remove current
      if self.mCurEditorWidget:
         self.vboxlayout2.removeWidget(self.mCurEditorWidget)
         self.mCurEditorWidget.setParent(None)
      else:
         self.vboxlayout2.removeWidget(self.mFcdEditArea)
         self.mFcdEditArea.setParent(None)
      
      # Add new
      if widget:
         self.vboxlayout2.addWidget(widget)         
      else:
         self.vboxlayout2.addWidget(self.mFcdEditArea)
      self.mCurEditorWidget = widget
      

class FCListModel(QtCore.QAbstractListModel):
   """ Model for reading and returning the list of active field containers in a scene.
       This model examines a scene object typemap and returns the found data.
   """
   def __init__(self, plugin, parent=None):
      QtCore.QAbstractListModel.__init__(self, parent)
      self.scene = None
      self.mFCList = []
      self.plugin = plugin
   
   def setScene(self, scene):
      """ Set to a new scene.  Reset the views. """
      self.scene = scene
      self.updateFCList()
      #self.reset()
   
   def updateFCList(self):
      """ Update the field container list by pulling entries from typemap. """
      
      # XXX: Update this code to use the type information
      self.mFCList = []
      if not self.scene:
         return
      fc_map = self.scene.mFcTypeMap
      
      filter_type = self.plugin.mFilterType      
      type_names = [c.__name__ for c in osg_helpers.getDerivedTypes(filter_type) 
                             if hasattr(c,"create")]      
      for n in type_names:
         self.mFCList.extend(fc_map.get(n,[]))
       
      # Sort the list in order of creation
      #self.mMaterialList.sort(lambda x,y:cmp(osg2.getContainerId(x),osg2.getContainerId(y)))  
      sort_list = [(osg2.getContainerId(c),c) for c in self.mFCList]
      sort_list.sort()
      self.mFCList[:] = [s[1] for s in sort_list]
      
      self.reset()


   def flags(self, index):
      if not index.isValid():
         return QtCore.Qt.ItemIsEnabled
      return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
   
   def rowCount(self, parent):
      if not self.scene:
         return 0
      else:
         return len(self.mFCList)
   
   def headerData(self, col, orientation, role):
      # Update this to say the type
      if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
         return QtCore.QtString("Field Container")
      return QtCore.QVariant()

   def data(self, index, role):
      """ Return the data for the index. 
      
      DisplayRole: Return text name of field container
      UserRole: Return field container object
      """
      if (not self.scene) or (not index.isValid()):
         return QtCore.QVariant()
      
      row = index.row()
      if row >= self.rowCount(None):
         return QtCore.QVariant()
      
      if QtCore.Qt.DisplayRole == role:
         fc = self.mFCList[row]
         fc_name = osg_helpers.getFcName(fc)
         if not fc_name:
            fc_name = "<none>"
         fc_type_name = fc.getType().getCName()
         text = "%s [%s]"%(fc_name, fc_type_name)
         return QtCore.QVariant(text)
      elif QtCore.Qt.EditRole == role:
         return QtCore.QVariant(str(osg_helpers.getFcName(self.mFCList[row])))
      elif QtCore.Qt.UserRole == role:
         fc = self.mFCList[row]
         return fc
   
      return QtCore.QVariant()

   def setData(self, index, value, role):
      row = index.row()
      if not index.isValid():
         return False
      elif QtCore.Qt.EditRole == role:
         fc = self.mFCList[row]
         osg_helpers.setFcName(fc, str(value.toString()))
         self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index)
         self.emit(QtCore.SIGNAL("dataChanged(int)"), row)
         return True
      return False




   